package cn.dengta.webapp.user.entity;

import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import cn.dengta.common.context.Context;
import cn.dengta.common.context.I18N;
import cn.dengta.common.model.*;
import cn.dengta.common.security.*;
import cn.dengta.common.util.Formats;
import cn.dengta.context.auth.Permission;
import cn.dengta.context.model.*;
import cn.dengta.context.web.RequestContextImpl;
import cn.dengta.webapp.base.entity.BizEntity;
import cn.dengta.webapp.user.entity.ClientInfo.LoginWay;
import cn.dengta.webapp.user.form.UserLoginForm;
import cn.dengta.webapp.user.model.*;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import me.codeplayer.util.*;

@Setter
@Getter
@Accessors(chain = true)
public abstract class MemberContent extends BizEntity implements Member, BizFlag.WritableBizFlag {

	/** 用户名 */
	protected String username;
	/** 密码 */
	protected String password;
	/** 手机号码 */
	protected String phone;
	/** 手机号码认证状态 */
	protected Integer phoneStatus;
	/** 邮箱 */
	protected String email;
	/** 邮箱认证状态 */
	protected Integer emailStatus;
	/** 昵称 */
	protected String nickname;
	/** 证件类型 */
	protected CertType certType;
	/** 证件号码 */
	protected String certNo;
	/** 真实姓名 */
	protected String realName;
	/** 实名认证状态：1=已认证；0=未认证 */
	protected Integer realNameStatus;
	/** 生日 */
	protected Date birthday;
	/** 性别：1=男；0=女 */
	protected Gender gender;
	/** 用户头像 */
	protected String avatar;
	/** 头像认证状态：2=已认证；1=认证中；0=未认证 */
	protected Integer avatarStatus;
	/** 业务标识 */
	protected long bizFlag;
	/** 最后一次登录时间 */
	protected Date lastLoginTime;
	/** 冻结时间 */
	protected Date freezeTime;
	/** 最后一次登录方式 */
	protected LoginWay loginWay;
	/** 邀请码 */
	protected String inviteCode;
	/** 推荐注册的用户ID */
	protected Long inviteUserId;
	/** sessionId */
	protected String sessionId;

	/** 权限码 Set 集合 */
	protected transient Set<String> permissions;
	/** 路由码 Set 集合 */
	protected transient Set<String> routeCodes;

	/** 永久封禁时间：只要 freezeTime ≥ 该时间戳（毫秒值）即可视为永久封禁 */
	public static final long foreverFreezeTime = new EasyDate(2099, 12, 31).getTime();

	public MemberContent init(BizEntity target, Date now) {
		MemberContent input = (MemberContent) target;
		username = X.expectNotEmpty(input.username, username, input.phone, input.email);
		certNo = X.expectNotEmpty(certNo, input.certNo);
		certType = X.expectNotNull(certType, input.certType);
		//		password = X.expectNotEmpty(input.password, password);
		birthday = X.expectNotNull(input.birthday, birthday);
		nickname = X.expectNotNull(input.nickname, nickname, "");
		gender = X.expectNotNull(input.gender, gender);
		avatar = X.expectNotEmpty(input.avatar, avatar);
		avatarStatus = X.expectNotNull(input.avatarStatus, avatarStatus, 0);
		email = X.expectNotEmpty(input.email, email);
		emailStatus = X.expectNotNull(input.emailStatus, emailStatus, 0);
		phone = X.expectNotEmpty(input.phone, phone);
		phoneStatus = X.expectNotNull(input.phoneStatus, phoneStatus, 0);
		realName = X.expectNotEmpty(input.realName, realName);
		realNameStatus = X.expectNotNull(input.realNameStatus, realNameStatus, 0);
		inviteCode = X.expectNotEmpty(inviteCode, input.inviteCode);
		return this;
	}

	public String getCertNo_() {
		return Formats.anonymousCertNo(getCertNo());
	}

	/** 是否已通过实名认证 */
	public boolean isRealNamed() {
		return Status.YES.eq(this.realNameStatus);
	}

	/** 是否已通过邮箱认证 */
	public boolean isEmailed() {
		return Status.YES.eq(emailStatus);
	}

	/** 是否已通过手机认证 */
	public boolean isPhoned() {
		return Status.YES.eq(phoneStatus);
	}

	/** 获取星座 */
	@JSONField(serialize = false)
	public String getConstellation() {
		return Zodiac.calcConstellation(birthday);
	}

	/** 获取生肖 */
	public String getZodiac() {
		return Zodiac.calcZodiac(birthday);
	}

	public void addGender(Integer gender) {
		this.gender = Gender.of(gender);
	}

	public String getRealName_() {
		return Formats.anonymousRealName(realName);
	}

	/** 模糊用户名 */
	public String getUsername_() {
		return Formats.anonymousUsername(getUsername());
	}

	@Override
	public void setBizFlag(long flag) {
		this.bizFlag = flag;
	}

	@Override
	public void setSessionId(String sessionId) {
		this.sessionId = sessionId;
	}

	/**
	 * 设置密码
	 *
	 * @param password 密码
	 * @param original 是否为原始明文密码，如果为true，则内部会对其进行加密处理
	 */
	public void setPassword(String password, boolean original) {
		if (original) {
			password = UserFieldAutoGenerator.encryptPassword(username, password, addTime);
		}
		this.password = password;
	}

	/**
	 * 检测用户输入的登录密码是否有效
	 *
	 * @param inputPassword 用户输入的密码
	 * @param original 是否为原始明文密码，如果为true，则内部会对其进行加密处理
	 */
	public final boolean checkPassword(String inputPassword, boolean original) {
		return checkAnyPassword(getUsername(), inputPassword, getAddTime(), getPassword(), original);
	}

	/**
	 * 检测用户输入的登录密码是否有效
	 *
	 * @param inputPassword 用户输入的密码
	 * @param targetPassword 用于比较的已经过加密处理的密码
	 * @param original 是否为原始明文密码，如果为true，则内部会对其进行加密处理
	 */
	public static boolean checkAnyPassword(String username, String inputPassword, Date addTime, String targetPassword, boolean original) {
		if (StringUtil.isEmpty(inputPassword)) {
			return false;
		}
		if (original) {
			inputPassword = UserFieldAutoGenerator.encryptPassword(username, inputPassword, addTime);
		}
		return targetPassword.equals(inputPassword);
	}

	@Override
	public boolean hasPermission(String code, String routeCode, boolean checkRouteCode) {
		// ID为 1 的用户具有所有权限，权限码为"user"的方法允许所有登录用户访问
		if (getId() == Context.internal().getSystemSuperAdminId() || Permission.USER.equals(code)) {
			return true;
		} else if (Permission.ADMIN.equals(code)) { // 权限码为"admin"的方法只允许管理员(含客服)用户访问
			return true;
		} else {
			final Set<String> permissions = getPermissions();
			boolean hasPermission = permissions != null && permissions.contains(code);
			if (checkRouteCode) {
				hasPermission = Boolean.TRUE.equals(X.map(getRouteCodes(), r -> r.contains(routeCode)));
			}
			return hasPermission;
		}
	}

	public Set<String> getPermissions() {
		return permissions;
	}

	@Override
	public boolean isFrozen() {
		return freezeTime != null && freezeTime.getTime() > System.currentTimeMillis();
	}

	@Override
	public FreezeTip getFreezeTip() {
		if (!isFrozen()) {
			return null;
		}
		final boolean foreverFrozen = freezeTime.getTime() >= User.foreverFreezeTime; // 永久封禁
		String tip = AppContext.inter() ? I18N.msg(UserI18nKey.FREEZE_TIME_CHINA_TIP, id.toString()) : I18N.msg(UserI18nKey.FREEZE_TIME_TIP, Formats.formatDefDate(freezeTime));
		WesKit wesKit = null;
		if (this instanceof User) {
			wesKit = ((User) this).getWesKit();
		}
		return FreezeTip.of(wesKit, tip, foreverFrozen ? null : freezeTime, id);
	}

	@Override
	public boolean canLogin() {
		return isEnabled() && UserStatus.canLogin(getStatus());
	}

	@Override
	public boolean isDestroyed() {
		return UserStatus.INVALID.eq(getStatus());
	}

	/** 是否存在邀请人 */
	public boolean hasInvite() {
		return Bean.isValidId(inviteUserId);
	}

	public static final String STATUS_LOGIN_ERROR = "invalid";

	/**
	 * // "$ip-$username"
	 */
	public static String makeKeyForUserLogin(UserLoginForm form) {
		RequestContextImpl req = RequestContextImpl.get();
		return req.clientIP() + '-' + form.username.toLowerCase();
	}

	public static MetaBasedAbuseDefender<UserLoginForm, Messager<MemberContent>> LOGIN = new MetaBasedAbuseDefender<>(
			new AbuseDefender.Threshold(5, 15),
			AbuseDefenderManager.LOGIN_ERROR_CONFIG_KEY, "maxTimes", "interval", TimeUnit.MINUTES,
			MemberContent::makeKeyForUserLogin,
			AbuseDefenderManager.mapMessagerState(STATUS_LOGIN_ERROR::equals),
			AbuseDefenderManager.stateMessagerMapper("login")
	);

	public static final String USERNAME = "username";
	public static final String PHONE = "phone";
	public static final String TYPE = "type";
	public static final String EMAIL = "email";
	public static final String NICKNAME = "nickname";
	public static final String CERT_NO = "cert_no";
	public static final String REAL_NAME = "real_name";
	public static final String REAL_NAME_STATUS = "real_name_status";
	public static final String PHONE_STATUS = "phone_status";
	public static final String BIRTHDAY = "birthday";
	public static final String GENDER = "gender";
	public static final String AVATAR = "avatar";
	public static final String AVATAR_STATUS = "avatar_status";
	public static final String INVITE_USER_ID = "invite_user_id";
	public static final String LOGIN_WAY = "login_way";
	public static final String FREEZE_TIME = "freeze_time";
	public static final String CLIENT = "client";
	public static final String LAST_LOGIN_TIME = "last_login_time";
	public static final String BIZ_FLAG = "biz_flag";

}
